﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace Zero.UControl.Component
{
    /// <summary>
    /// SmartTextBox.xaml 的交互逻辑
    /// </summary>
    public partial class SmartTextBox : UserControl, INotifyPropertyChanged
    {
        public SmartTextBox()
        {
            InitializeComponent();
            
            // 针对控件内部存在自身行为的情况为了使得这些事件能够生效，而不被控件自身的一些内部行为影响，要使用隧道事件来避免事件被吞掉
            PART_TextBox.PreviewKeyDown += TextBox_PreviewKeyDown;
            PART_TextBox.PreviewMouseLeftButtonUp += PreviewMouseLeftButtonUp;

            PART_ListBox.PreviewKeyDown += PART_ListBox_PreviewKeyDown;
            PART_ListBox.PreviewMouseLeftButtonUp += ListBox_MouseLeftButtonUp;

            Loaded += SmartTextBox_Loaded;
            Unloaded += SmartTextBox_Unloaded;
        }

        #region 依赖属性
        public ObservableCollection<string> Suggestions
        {
            get => (ObservableCollection<string>)GetValue(SuggestionsProperty);
            set => SetValue(SuggestionsProperty, value);
        }

        public static readonly DependencyProperty SuggestionsProperty =
          DependencyProperty.Register("Suggestions", typeof(ObservableCollection<string>), typeof(SmartTextBox),
              new PropertyMetadata(null));


        public string Text
        {
            get { return (string)GetValue(TextProperty); }
            set { SetValue(TextProperty, value); }
        }

        public static readonly DependencyProperty TextProperty =
            DependencyProperty.Register("Text", typeof(string), typeof(SmartTextBox), new PropertyMetadata("", OnTextChanged));

        #endregion

        #region 普通属性
        private bool _isPopupOpen;
        public bool IsPopupOpen
        {
            get => _isPopupOpen;
            set
            {
                _isPopupOpen = value;
                OnPropertyChanged(nameof(IsPopupOpen));
            }
        }

        public ObservableCollection<string> FilteredSuggestions { get; set; } = new ObservableCollection<string>();

        #endregion

        #region 事件方法
        private void UpdateSuggestions()
        {
            FilteredSuggestions.Clear();
            if (Suggestions != null && !string.IsNullOrEmpty(Text))
            {
                var matches = Suggestions.Where(s => s.StartsWith(Text, System.StringComparison.InvariantCultureIgnoreCase));
                foreach (var match in matches)
                    FilteredSuggestions.Add(match);

                IsPopupOpen = FilteredSuggestions.Any();
            }
            else
            {
                IsPopupOpen = false;
            }
        }

        private static void OnTextChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var tbox = d as SmartTextBox;
            tbox.UpdateSuggestions();
        }

        private void TextBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Down)
            {
                if (FilteredSuggestions.Any())
                {
                    if (!PART_ListBox.IsKeyboardFocusWithin)
                    {
                        PART_ListBox.Focus();
                        PART_ListBox.SelectedIndex = 0;
                    }
                    e.Handled = true;
                }
            }
            else if (e.Key == Key.Escape)
            {
                if (IsPopupOpen)
                {
                    IsPopupOpen = false;
                    e.Handled = true;
                }
            }
        }

        private void PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            FilteredSuggestions.Clear();
            foreach (var item in Suggestions)
            {
                FilteredSuggestions.Add(item);
            }
            IsPopupOpen = true;
        }

        private void ListBox_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (PART_ListBox.SelectedItem is string selected)
            {
                SetCurrentValue(TextProperty, selected);
                IsPopupOpen = false;
            }
        }

        private void PART_ListBox_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (e.Key == Key.Enter)
            {
                if (PART_ListBox.IsKeyboardFocusWithin && PART_ListBox.SelectedItem is string selectedFromList)
                {
                    SetCurrentValue(TextProperty, selectedFromList);
                    IsPopupOpen = false;
                    e.Handled = true;

                    PART_TextBox.CaretIndex = selectedFromList.Length;
                    PART_TextBox.Focus();
                }
            }
            else if (e.Key == Key.Escape)
            {
                if (IsPopupOpen)
                {
                    IsPopupOpen = false;
                    e.Handled = true;
                    PART_TextBox.CaretIndex = PART_TextBox.Text.Length;
                    PART_TextBox.Focus();
                }
            }
        }

        private void SmartTextBox_Loaded(object sender, RoutedEventArgs e)
        {
            var window = Window.GetWindow(this);
            window.MouseDown += OnWindowMouseDown;
            window.LocationChanged += Window_LocationChanged;
        }

        private void SmartTextBox_Unloaded(object sender, RoutedEventArgs e)
        {
            var window = Window.GetWindow(this);
            window.MouseDown -= OnWindowMouseDown;
            window.LocationChanged -= Window_LocationChanged;
        }

        private void Window_LocationChanged(object? sender, EventArgs e)
        {
            if (PART_Popup.IsOpen)
            {
                // 改变offset的值，强制其触发内部的UpdatePosition方法，进行重绘
                var offset = PART_Popup.HorizontalOffset;
                PART_Popup.HorizontalOffset = offset + 1;
                PART_Popup.HorizontalOffset = offset;
            }
        }

        private void OnWindowMouseDown(object sender, MouseButtonEventArgs e)
        {
            if (!IsPopupOpen)
                return;

            // 获取当前点击的元素
            var clickedElement = Mouse.DirectlyOver as DependencyObject;

            if (!IsDescendantOfThisControl(clickedElement))
            {
                IsPopupOpen = false;
            }
        }

        // 判断元素是否是当前控件的子元素
        private bool IsDescendantOfThisControl(DependencyObject element)
        {
            while (element != null)
            {
                if (element == this)
                    return true;
                element = VisualTreeHelper.GetParent(element);
            }
            return false;
        }

        #endregion


        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string prop) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
    }
}
